import * as PIXI from "pixi.js";
import {
  Config,
  PixijsvueComponentConfig,
  Plugin,
  PluginData,
} from "../../typings/main";
import Dep from "../dep";
import { deepProxy, getGoalData, elAnalysis } from "../../utils/index";
import { forEach } from "../Traverse/pixijsvueTra";
import Resource from "../ResourceCenter";
import GlobalResource from "../ResourceGlobal";
import { Hook } from "../hook";
import { DepItem, FoundDependence } from "../../typings/dep";
import { Directives } from '../directives';
import { initComputed, initData, initDep, initGlobalResource, initMethod, initPlugin, initResource, initRootEle, initStyle, initWatcher } from "./init";

let _globalComponents: Record<string, PixijsvueComponentConfig> = {};
class Pixijsvue {
  [propName: string]: any;
  public $vm!: PIXI.Container;
  protected $computed: Record<string, Function> = {};
  public $data: Record<string, any> = {};
  protected $style: Record<string, Function> = {};
  protected $children: Array<any> = [];
  protected $rootElement!: Element | HTMLElement | null;
  protected $dep!: Dep;
  protected $watcher: Record<string, Record<string, Array<Function>>> = {};
  protected $methods: Record<string, Function> = {};
  protected $resource: Resource | undefined;
  public _depList: Array<DepItem> = [];
  protected $hook: Hook = new Hook();
  protected _data: Record<string, any> = {};
  protected $directives: Directives = new Directives();

  protected $options: Config;
  protected $cid: number = 0;
  protected $id: number = 0;
  protected $root: Pixijsvue = this;
  public $globalDirective?: any;
  public $globalResource?: GlobalResource;
  public _globalComponents: Record<string, PixijsvueComponentConfig> =
    _globalComponents;
  public static __proto__: any;
  static use: Function;
  static mixin: Function;
  static plugins: PluginData = {
    mixin: [],
  };
  constructor(config: Config) {
    this.$options = config;
    this.$hook.addHooks("beforeCreate", config.beforeCreate);
    this.$hook.addHooks("created", config.created);
    this.$hook.addHooks("beforeMounte", config.beforeMounte);
    this.$hook.addHooks("mounted", config.mounted);
    this.initPlugin();
    this.initData();
    this.initRootEle();
    this.initWatcher();
    this.initDep();
    this.initResource();
    this.initMethod();
    this.initStyle();

    this.$hook.useHooks(this, "beforeCreate");
    this.initGlobalResource();
    this.configData();
    this.initComputed();
    this.$hook.useHooks(this, "created");
  }
  protected emit(eventName: string, ...argvs: Array<any>) {
    if (this.$listener) {
      if (this.$listener[eventName]) {
        this.$listener[eventName].apply(this.$parent, argvs);
      }
    }
  }
  public getComponentId() {
    return ++this.$cid;
  }
  public async loaded() {
    if (this.$globalResource) {
      await this.$globalResource.init();
    }
    this.$hook.useHooks(this, "beforeMounte");
    this.traverse();
    this.$hook.useHooks(this, "mounted");
  }
  private initData = initData;

  private initPlugin = initPlugin;

  private initRootEle = initRootEle;

  private initWatcher = initWatcher;

  private initDep = initDep;
  
  private initResource = initResource;

  private initMethod = initMethod;
  private initStyle = initStyle;
  private initGlobalResource = initGlobalResource;
  private initComputed = initComputed;
  
  private configData() {
    let get = (path: string) => {
      this.$dep.get(path);
    };
    let set = (path: string, value: any) => {
      let deps: Array<FoundDependence>;
      /**
       * 我们认为数组的特点是键名为数字字符串
       * 所以检查键名是否为数字字符串(通过Number.NaN(num)判断)
       * 然后进行依赖查询.
       * TODO
       */
      let num = Number(path.split(".").pop());
      if (!Number.isNaN(num)) {
        let arr_path: Array<string> = path.split(".");
        // arr_path.pop();
        deps = this.$dep.findDepValue(String(arr_path).replace(/,/g, "."));
      } else if (path.includes(".__proto__")) {
        /**
         * 由于将proxy上的数组原型上的方法重写。
         * 每次调用数组上的函数时，都会零时创建一个变量，深拷贝proxy上的数组
         * 这个深拷贝的数组会调用，原本应该由proxy上的数组调用的方法。
         * 最后将这个临时的数组进行深代理deepProxy(arr, get, set, path + key + ".");
         * 覆盖原来proxy上的数组。
         * 此时我们需要将刚刚深代理的数组上的方法重写，obj[key].__proto__ = arrayMethod;（file: deepProxy.ts）
         * set方法中就出现了.__proto__这个键名
         */
        deps = this.$dep.findDepValue(<string>path.replace(".__proto__", ""));
        /**
         * 被修改属性的响应
         */
        if (this.$watcher[<string>path.replace(".__proto__", "")]) {
          this.$watcher["data"][<string>path.replace(".__proto__", "")].forEach(
            (fun: Function) => {
              fun(
                getGoalData(proxy, <string>path.replace(".__proto__", "")),
                this.$id,
                "data"
              );
            }
          );
        }
      } else {
        deps = this.$dep.findDepValue(path);
      }
      /**
       * 被修改属性的响应
       */
      if (this.$watcher["data"][path]) {
        this.$watcher["data"][path].forEach((fun: Function) => {
          console.log(getGoalData(proxy, path));
          fun(getGoalData(proxy, path), this.$id, "data");
        });
      }
      /**
       * 将依赖于被修改属性的计算属性，重新计算并存入proxy中
       * 计算属性响应
       */
      // console.log(data);
      // console.log("?")
      // console.log(dep)
      deps.forEach((depItem) => {
        if (this.$computed[depItem.value]) {
          proxy[depItem.value] = this.$computed[depItem.value].call(this);
        }
        this.$watcher[depItem.depName][depItem.value].forEach(
          (item: Function) => {
            item(proxy[depItem.value], this.$id, depItem.depName);
          }
        );
      });
    };
    let proxy = deepProxy(this._data, get, set, "");
    this.$data = proxy;
  }
  private traverse() {
    if (this.$rootElement) {
      this.$vm = forEach.call(this, this.$rootElement);
    }
  }
}
export { Pixijsvue }